#include "kvdb/server/kvdbsession.h"

namespace kvdb {

const char KVDB_RESULT_SUCCEED_BUFFER[1] = {KVDB_RESULT_SUCCEED};
const char KVDB_RESULT_FAILURE_BUFFER[1] = {KVDB_RESULT_FAILURE};

KvdbSession::KvdbSession(vzconn::VzConnect::Ptr tcp_connect,
                         KvdbSqlite::Ptr kvdb_sqlite)
  : tcp_connect_(tcp_connect),
    io_service_(tcp_connect->io_service()),
    kvdb_sqlite_(kvdb_sqlite) {
}

KvdbSession::~KvdbSession() {
  LOG(WARNING) << "Delete KvdbSession";
}

bool KvdbSession::StartSession() {
  BOOST_ASSERT(tcp_connect_);
  tcp_connect_->SignalConnectError.connect(
    boost::bind(&KvdbSession::OnConnectError, shared_from_this(), _1, _2));
  tcp_connect_->SignalConnectWrite.connect(
    boost::bind(&KvdbSession::OnConnectWrite, shared_from_this(), _1));
  tcp_connect_->SignalConnectRead.connect(
    boost::bind(&KvdbSession::OnConnectRead, shared_from_this(), _1, _2, _3, _4));
  return true;
}

void KvdbSession::StopSession() {
}

void KvdbSession::OnConnectError(vzconn::VzConnect::Ptr connect,
                                 const boost::system::error_code& err) {
  tcp_connect_.reset();
  SignalSessionError(shared_from_this(), err);
}

void KvdbSession::OnConnectWrite(vzconn::VzConnect::Ptr connect) {
}

void KvdbSession::OnConnectRead(vzconn::VzConnect::Ptr connect,
                                const char* buffer, int size, int flag) {
  if(!stanza_.Parse(buffer, size)) {
    LOG(ERROR) << "Parse the data error";
    return;
  }
  if(!ProcessStanza()) {
    LOG(ERROR) << "Failure to Process Stanza";
  }
}

bool KvdbSession::ProcessStanza() {
  switch (stanza_.queury_type_) {
  case kvdb::KVDB_REPLACE:
    // LOG(INFO) << "kvdb::KVDB_REPLACE";
    return ReplaceKeyValue();
    break;
  case kvdb::KVDB_DELETE:
    // LOG(INFO) << "kvdb::KVDB_DELETE";
    return DeleteKeyValue();
    break;
  case kvdb::KVDB_SEARCH:
    // LOG(INFO) << "kvdb::KVDB_SEARCH";
    return SelectKeyValue();
    break;
  }
  // LOG(INFO) << "Ending";
  return false;
}


bool KvdbSession::ReplaceKeyValue() {
  bool res = kvdb_sqlite_->ReplaceKeyValue(&stanza_.key_[0],
             stanza_.key_size_,
             &stanza_.value_[0],
             stanza_.value_size_);
  if(res) {
    ResponseSucceed();
  } else {
    ResponseFailure();
  }
  return res;
}

bool KvdbSession::DeleteKeyValue() {
  bool res = kvdb_sqlite_->DeleteKeyValue(&stanza_.key_[0], stanza_.key_size_);
  if(res) {
    ResponseSucceed();
  } else {
    ResponseFailure();
  }
  return res;
}

bool KvdbSession::SelectKeyValue() {
  bool res = kvdb_sqlite_->SelectKeyValue(&stanza_.key_[0], stanza_.key_size_,
                                          buffer_);
  if(res) {
    buffer_[0] = KVDB_RESULT_SUCCEED;
    uint32 size = buffer_.size() - 5;
    size = htonl(size);
    memcpy(&buffer_[1], &size, sizeof(uint32));
    WriteResponse(&buffer_[0], buffer_.size());
  } else {
    ResponseFailure();
  }
  return res;
}

void KvdbSession::ResponseSucceed() {
  WriteResponse(KVDB_RESULT_SUCCEED_BUFFER, 1);
}

void KvdbSession::ResponseFailure() {
  WriteResponse(KVDB_RESULT_FAILURE_BUFFER, 1);
}

void KvdbSession::WriteResponse(const char *data, std::size_t data_size) {
  if(tcp_connect_->IsConnected()) {
    tcp_connect_->AsyncWrite(data, data_size);
  } else {
    LOG(ERROR) << "Tcp disconnected";
  }
}

////////////////////////////////////////////////////////////////////////////////

KvdbSession::QueryStanza::QueryStanza() {
  ResetStanza();
}

KvdbSession::QueryStanza::~QueryStanza() {
}

bool KvdbSession::QueryStanza::Parse(const char* buffer, int size) {
  ResetStanza();
  int offset = 0;
  // Read the type
  if(offset + 1 >= size) {
    LOG(ERROR) << "The size of recv error";
    return false;
  }
  queury_type_ = buffer[offset];
  offset++;
  // Read the key size
  if(queury_type_ == KVDB_NONE || offset + 1 >= size) {
    LOG(ERROR) << "The size of recv error";
    return false;
  }
  key_size_ = buffer[offset];
  if(key_size_ <= 0) {
    LOG(ERROR) << "Read the size of key error";
    return false;
  }
  offset++;
  // Read the value size
  if(offset + 4 >= size) {
    LOG(ERROR) << "The size of recv error";
    return false;
  }
  memcpy(&value_size_, buffer + offset, sizeof(uint32));
  value_size_ = ntohl(value_size_);
  offset += sizeof(uint32);
  // Read the key
  if(offset + key_size_ > size) {
    LOG(ERROR) << "The size of recv error";
    return false;
  }
  key_.resize((uint32)key_size_);
  memcpy(&key_[0], buffer + offset, key_size_);
  offset += key_size_;
  // Read the value
  if(offset + value_size_ != size) {
    LOG(ERROR) << "The size of recv error";
    return false;
  }
  if(value_size_ != 0) {
    value_.resize(value_size_);
    memcpy(&value_[0], buffer + offset, value_size_);
    offset += value_size_;
  }
  if(offset != size) {
    LOG(ERROR) << "The size is big than data";
    return false;
  }
  return true;
}

}